LIBRERÍAS¶
#instalar mglearn en su ambiente para Machine Learning
#pip install mglearn
import warnings
warnings.filterwarnings("ignore")
import pandas as pd
import mglearn
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
X, y = mglearn.datasets.make_forge()
from sklearn.model_selection import train_test_split
from scipy.stats import kurtosis
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.datasets import make_blobs
#Trazar el conjunto de datos
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
#loc=4 especifica la ubicación de la leyenda en el gráfico (esquina inferior derecha)
plt.legend(["Class 0", "Class 1"], loc=4)
plt.xlabel("First feature")
plt.ylabel("Second feature")
print("X.shape: {}".format(X.shape))
X.shape: (26, 2)
Esta línea de código utiliza la función mglearn.discrete_scatter para crear un gráfico de dispersión. Toma los valores de las dos primeras columnas de la matriz X (columna 0 y columna 1) como las coordenadas x e y de los puntos de datos y utiliza los valores de la matriz y para colorear los puntos según sus clases o etiquetas. En este caso, se asume que tienes dos clases (Class 0 y Class 1).Como se puede ver en X.shape, este dataset consta de 26 puntos de datos, con 2 características.
Para ilustrar los algoritmos de regresión, utilizaremos el dataset sintético wave. Este dataset tiene una única característica de entrada y una variable objetivo continua (o respuesta) que queremos modelar.
X, y = mglearn.datasets.make_wave(n_samples=40)
plt.plot(X, y, '*')
plt.ylim(-3, 3)
plt.xlabel("Feature")
plt.ylabel("Target");
Complementaremos estos pequeños datasets sintéticos con dos conjuntos de datos del mundo real que se incluyen en scikit-learn. Uno de ellos es el conjunto de datos de cáncer de mama de Wisconsin (cancer, para abreviar), que registra mediciones clínicas de tumores de cáncer de mama. Cada tumor se etiqueta como benign (para tumores inofensivos) o malignant (para tumores cancerosos).
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
print("cancer.keys(): \n{}".format(cancer.keys()))
# Esta línea de código imprime las claves (keys) del objeto cancer. En scikit-learn, los conjuntos de datos cargados
# #suelen ser diccionarios de Python, y esta línea muestra las claves disponibles en ese diccionario.
# #Esto es útil para conocer la estructura de los datos cargados.
cancer.keys(): dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module'])
print("Shape of cancer data: {}".format(cancer.data.shape))
Shape of cancer data: (569, 30)
cancer.target_names
array(['malignant', 'benign'], dtype='<U9')
print("Conteo de la muestra por clases:\n{}".format(
{n: v for n, v in zip(cancer.target_names, np.bincount(cancer.target))}))
Conteo de la muestra por clases:
{'malignant': 212, 'benign': 357}
{n: v for n, v in zip(cancer.target_names, np.bincount(cancer.target))}: Esta expresión crea un diccionario de Python que muestra la cantidad de muestras por clase en el conjunto de datos. Utiliza la función np.bincount de NumPy para contar las ocurrencias de cada valor en el arreglo cancer.target, que contiene las etiquetas de las muestras. Luego, utiliza zip para combinar estas cuentas con los nombres de las clases, que se encuentran en cancer.target_names. El resultado es un diccionario donde las claves son los nombres de las clases y los valores son las cantidades de muestras de cada clase.
print("Feature names:\n{}".format(cancer.feature_names))
Feature names: ['mean radius' 'mean texture' 'mean perimeter' 'mean area' 'mean smoothness' 'mean compactness' 'mean concavity' 'mean concave points' 'mean symmetry' 'mean fractal dimension' 'radius error' 'texture error' 'perimeter error' 'area error' 'smoothness error' 'compactness error' 'concavity error' 'concave points error' 'symmetry error' 'fractal dimension error' 'worst radius' 'worst texture' 'worst perimeter' 'worst area' 'worst smoothness' 'worst compactness' 'worst concavity' 'worst concave points' 'worst symmetry' 'worst fractal dimension']
VECINOS MÁS CERCANOS: (k-nearest neighbors) (k-NN)¶
Su enfoque consiste en memorizar el conjunto de entrenamiento y luego predecir la etiqueta del vecino más cercano en dicho conjunto. Este método se basa en la idea de que las características utilizadas para describir los puntos en el dominio son relevantes para determinar sus etiquetas, de manera que es probable que puntos cercanos tengan la misma etiqueta
Clasificación -vecinos: En su versión más sencilla, el algoritmo k-NN sólo considera exactamente un vecino más cercano, que es el dato de entrenamiento más cercano al punto para el que queremos hacer una predicción. La predicción es entonces simplemente la salida conocida para este punto de entrenamiento.
mglearn.plots.plot_knn_classification(n_neighbors=1)
En lugar de considerar sólo al vecino más cercano, también podemos considerar un número arbitrario , k de vecinos. De ahí viene el nombre del algoritmo k-vecinos más cercanos. Cuando se considera más de un vecino, se utiliza la votación para asignar una etiqueta. Esto significa que, para cada punto de prueba, contamos cuántos vecinos pertenecen a clase 0 y cuántos vecinos pertenecen a la clase 1. A continuación, asignamos la clase que es más frecuente: es decir, la clase mayoritaria entre los k vecinos más cercanos
mglearn.plots.plot_knn_classification(n_neighbors=4)
En primer lugar, dividimos nuestros datos en un conjunto de entrenamiento y otro de prueba para poder evaluar el rendimiento de la generalización. random_state=0 nos asegura que obtendremos los mismos conjuntos de training y test para diferentes ejecuciones
from sklearn.model_selection import train_test_split
#cuando no es suministrado el porcentaje de entrenamiento train_test_split considera este
# #porcentaje para el test como el 25%, esto es test_size=0.25
cancer.target.shape
(569,)
df.insert(0, 'diagnosis', cancer.target): Esta línea de código inserta una nueva columna llamada 'diagnosis' en el DataFrame df en la posición 0 (es decir, al principio del DataFrame). La columna 'diagnosis' se llena con los valores de cancer.target, que son las etiquetas de las muestras y representan el diagnóstico de cáncer (0 para benigno y 1 para maligno).
df = pd.DataFrame(cancer.data, columns = cancer.feature_names)
df.insert(0, 'diagnosis', cancer.target)
df.head()
| diagnosis | mean radius | mean texture | mean perimeter | mean area | mean smoothness | mean compactness | mean concavity | mean concave points | mean symmetry | ... | worst radius | worst texture | worst perimeter | worst area | worst smoothness | worst compactness | worst concavity | worst concave points | worst symmetry | worst fractal dimension | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 17.99 | 10.38 | 122.80 | 1001.0 | 0.11840 | 0.27760 | 0.3001 | 0.14710 | 0.2419 | ... | 25.38 | 17.33 | 184.60 | 2019.0 | 0.1622 | 0.6656 | 0.7119 | 0.2654 | 0.4601 | 0.11890 |
| 1 | 0 | 20.57 | 17.77 | 132.90 | 1326.0 | 0.08474 | 0.07864 | 0.0869 | 0.07017 | 0.1812 | ... | 24.99 | 23.41 | 158.80 | 1956.0 | 0.1238 | 0.1866 | 0.2416 | 0.1860 | 0.2750 | 0.08902 |
| 2 | 0 | 19.69 | 21.25 | 130.00 | 1203.0 | 0.10960 | 0.15990 | 0.1974 | 0.12790 | 0.2069 | ... | 23.57 | 25.53 | 152.50 | 1709.0 | 0.1444 | 0.4245 | 0.4504 | 0.2430 | 0.3613 | 0.08758 |
| 3 | 0 | 11.42 | 20.38 | 77.58 | 386.1 | 0.14250 | 0.28390 | 0.2414 | 0.10520 | 0.2597 | ... | 14.91 | 26.50 | 98.87 | 567.7 | 0.2098 | 0.8663 | 0.6869 | 0.2575 | 0.6638 | 0.17300 |
| 4 | 0 | 20.29 | 14.34 | 135.10 | 1297.0 | 0.10030 | 0.13280 | 0.1980 | 0.10430 | 0.1809 | ... | 22.54 | 16.67 | 152.20 | 1575.0 | 0.1374 | 0.2050 | 0.4000 | 0.1625 | 0.2364 | 0.07678 |
5 rows × 31 columns
Aquí 0=malignant, 1=benign. Verifiquemos que tipos de datos contiene el dataset. La función info() proporciona información sobre los tipos de datos, columnas, recuento de valores nulos, uso de memoria, etc.
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 569 entries, 0 to 568 Data columns (total 31 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 diagnosis 569 non-null int32 1 mean radius 569 non-null float64 2 mean texture 569 non-null float64 3 mean perimeter 569 non-null float64 4 mean area 569 non-null float64 5 mean smoothness 569 non-null float64 6 mean compactness 569 non-null float64 7 mean concavity 569 non-null float64 8 mean concave points 569 non-null float64 9 mean symmetry 569 non-null float64 10 mean fractal dimension 569 non-null float64 11 radius error 569 non-null float64 12 texture error 569 non-null float64 13 perimeter error 569 non-null float64 14 area error 569 non-null float64 15 smoothness error 569 non-null float64 16 compactness error 569 non-null float64 17 concavity error 569 non-null float64 18 concave points error 569 non-null float64 19 symmetry error 569 non-null float64 20 fractal dimension error 569 non-null float64 21 worst radius 569 non-null float64 22 worst texture 569 non-null float64 23 worst perimeter 569 non-null float64 24 worst area 569 non-null float64 25 worst smoothness 569 non-null float64 26 worst compactness 569 non-null float64 27 worst concavity 569 non-null float64 28 worst concave points 569 non-null float64 29 worst symmetry 569 non-null float64 30 worst fractal dimension 569 non-null float64 dtypes: float64(30), int32(1) memory usage: 135.7 KB
df.describe()
| diagnosis | mean radius | mean texture | mean perimeter | mean area | mean smoothness | mean compactness | mean concavity | mean concave points | mean symmetry | ... | worst radius | worst texture | worst perimeter | worst area | worst smoothness | worst compactness | worst concavity | worst concave points | worst symmetry | worst fractal dimension | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | ... | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 | 569.000000 |
| mean | 0.627417 | 14.127292 | 19.289649 | 91.969033 | 654.889104 | 0.096360 | 0.104341 | 0.088799 | 0.048919 | 0.181162 | ... | 16.269190 | 25.677223 | 107.261213 | 880.583128 | 0.132369 | 0.254265 | 0.272188 | 0.114606 | 0.290076 | 0.083946 |
| std | 0.483918 | 3.524049 | 4.301036 | 24.298981 | 351.914129 | 0.014064 | 0.052813 | 0.079720 | 0.038803 | 0.027414 | ... | 4.833242 | 6.146258 | 33.602542 | 569.356993 | 0.022832 | 0.157336 | 0.208624 | 0.065732 | 0.061867 | 0.018061 |
| min | 0.000000 | 6.981000 | 9.710000 | 43.790000 | 143.500000 | 0.052630 | 0.019380 | 0.000000 | 0.000000 | 0.106000 | ... | 7.930000 | 12.020000 | 50.410000 | 185.200000 | 0.071170 | 0.027290 | 0.000000 | 0.000000 | 0.156500 | 0.055040 |
| 25% | 0.000000 | 11.700000 | 16.170000 | 75.170000 | 420.300000 | 0.086370 | 0.064920 | 0.029560 | 0.020310 | 0.161900 | ... | 13.010000 | 21.080000 | 84.110000 | 515.300000 | 0.116600 | 0.147200 | 0.114500 | 0.064930 | 0.250400 | 0.071460 |
| 50% | 1.000000 | 13.370000 | 18.840000 | 86.240000 | 551.100000 | 0.095870 | 0.092630 | 0.061540 | 0.033500 | 0.179200 | ... | 14.970000 | 25.410000 | 97.660000 | 686.500000 | 0.131300 | 0.211900 | 0.226700 | 0.099930 | 0.282200 | 0.080040 |
| 75% | 1.000000 | 15.780000 | 21.800000 | 104.100000 | 782.700000 | 0.105300 | 0.130400 | 0.130700 | 0.074000 | 0.195700 | ... | 18.790000 | 29.720000 | 125.400000 | 1084.000000 | 0.146000 | 0.339100 | 0.382900 | 0.161400 | 0.317900 | 0.092080 |
| max | 1.000000 | 28.110000 | 39.280000 | 188.500000 | 2501.000000 | 0.163400 | 0.345400 | 0.426800 | 0.201200 | 0.304000 | ... | 36.040000 | 49.540000 | 251.200000 | 4254.000000 | 0.222600 | 1.058000 | 1.252000 | 0.291000 | 0.663800 | 0.207500 |
8 rows × 31 columns
Realicemos tabla de frecuencias y diagrama de barras para nuestra variable respuesta
df.diagnosis.value_counts()
diagnosis 1 357 0 212 Name: count, dtype: int64
sns.set_style("ticks") #"whitegrid"
plt.title('Tipo de cancer: conteo')
sns.countplot(x=df.diagnosis)
plt.xlabel('Diagnóstico')
plt.ylabel('Frequency')
plt.show()
otras opciones: "darkgrid": Fondo gris oscuro con una cuadrícula.
"whitegrid": Fondo blanco con una cuadrícula.
"dark": Fondo gris oscuro sin cuadrícula.
"white": Fondo blanco sin cuadrícula.
"ticks": Fondo blanco con marcas de graduación de eje en las cuatro direcciones.
Nótese que nuestro dataset está desbalanceado. Existen técnicas como SMOTE y Stratified sampling, que pueden utilizarse para mejorar el score de clasificación de datos desbalanceados. Verifiquemos además si existen datos faltantes en nuestro Dataframe
df.isnull().sum()
diagnosis 0 mean radius 0 mean texture 0 mean perimeter 0 mean area 0 mean smoothness 0 mean compactness 0 mean concavity 0 mean concave points 0 mean symmetry 0 mean fractal dimension 0 radius error 0 texture error 0 perimeter error 0 area error 0 smoothness error 0 compactness error 0 concavity error 0 concave points error 0 symmetry error 0 fractal dimension error 0 worst radius 0 worst texture 0 worst perimeter 0 worst area 0 worst smoothness 0 worst compactness 0 worst concavity 0 worst concave points 0 worst symmetry 0 worst fractal dimension 0 dtype: int64
Pasamos a verficar si existe correlación entre las características: df.iloc[:, 1:]: Esta parte del código selecciona todas las filas (:) del DataFrame df y todas las columnas a partir de la segunda columna en adelante. En otras palabras, excluye la primera columna del DataFrame, que suele ser la columna que contiene las etiquetas o nombres de las muestras. corr.shape: Finalmente, se utiliza la propiedad .shape de la matriz de correlación corr para obtener sus dimensiones.
corr = df.iloc[: , 1:].corr()
corr.shape
(30, 30)
Esta parte del código crea una matriz de ceros con las mismas dimensiones que la matriz de correlación corr. En otras palabras, crea una matriz de ceros con el mismo número de filas y columnas que corr. Esto inicializa la matriz de máscara con todos los valores en cero.
mask[np.triu_indices_from(mask)] = True: Luego, se utiliza la función np.triu_indices_from() para obtener los índices de la mitad superior de la matriz (es decir, los índices de la triangular superior). Estos índices corresponden a los elementos que queremos ocultar en la visualización de la matriz de correlación. Finalmente, se establecen los valores en la matriz de máscara mask en True en los índices obtenidos en el paso anterior. Esto significa que todos los elementos en la triangular superior de la matriz de máscara se establecen en True, mientras que los elementos en la mitad inferior se mantienen en False
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
El código que proporcionaste utiliza la biblioteca Seaborn (sns) y Matplotlib (plt) para visualizar una matriz de correlación en forma de un mapa de calor (heatmap). Aquí hay una explicación de lo que hace este código:
sns.set(font_scale=1.8): Esta línea de código establece el tamaño de fuente para los elementos de la visualización utilizando la función sns.set. font_scale=1.8 aumenta el tamaño de fuente en 1.8 veces el valor predeterminado. Esto se hace para mejorar la legibilidad de los elementos en la visualización. sns.heatmap(corr, mask=mask, cbar=True, fmt='.1f', annot=True, annot_kws={'size': 15}, cmap='Reds'): Esta línea de código crea el mapa de calor utilizando la función sns.heatmap. Los argumentos y opciones utilizados son los siguientes:
corr: La matriz de correlación que se va a visualizar. mask=mask: La matriz de máscara que oculta la triangular superior de la matriz de correlación, como se explicó anteriormente. cbar=True: Muestra la barra de color (barra de colores) a la derecha del mapa de calor para indicar los valores de correlación. fmt='.1f': Formato de cadena para los valores mostrados en el mapa de calor, con una cifra decimal. annot=True: Muestra los valores de correlación en cada celda del mapa de calor. annot_kws={'size': 15}: Opciones para personalizar el tamaño del texto de los valores de correlación. cmap='Reds': El mapa de colores utilizado para representar los valores de correlación. En este caso, se utiliza el esquema de colores 'Reds', que va de tonos más claros a más oscuros.
'viridis': Un mapa de colores perceptualmente uniforme que va de tonos amarillos a verdes y azules.
'plasma': Un mapa de colores con transiciones suaves desde tonos oscuros hasta colores brillantes.
'inferno': Un mapa de colores que va desde el naranja oscuro hasta el amarillo brillante.
'magma': Un mapa de colores que comienza en tonos morados oscuros y termina en colores brillantes.
'cividis': Un mapa de colores diseñado para ser amigable con las personas con deficiencias visuales de color.
'coolwarm': Un mapa de colores que combina colores fríos y cálidos para resaltar tanto valores bajos como altos en el mapa de calor.
'Reds': Un mapa de colores que varía desde tonos claros hasta rojos oscuros.
'Blues': Un mapa de colores que varía desde tonos claros hasta azules oscuros.
sns.set(font_scale=1.8)
plt.figure(figsize=(20,20))
sns.heatmap(corr, mask=mask, cbar=True, fmt='.1f', annot=True, annot_kws={'size':15}, cmap='Blues');
Trazamos histogramas de cada característica en nuestro dataset
sns.set(font_scale=1.8)
df.iloc[: , 1:].hist(figsize = (30,30), color = 'Blue');
sns.set(font_scale=1.8)
plt.figure(figsize=(20,20))
sns.boxplot(data= df.drop(['diagnosis'], axis=1) ,width=0.5 , saturation=0.9, orient="h");
melted_data = pd.melt(df, id_vars="diagnosis", value_vars=['worst radius', 'worst texture', 'worst perimeter']): Esta línea de código utiliza la función pd.melt para transformar el DataFrame df. La función melt toma varias columnas y las "derrite" en una sola columna, mientras que mantiene una columna identificadora ('diagnosis' en este caso) como referencia. En este caso, las columnas 'worst radius,' 'worst texture,' y 'worst perimeter' se derriten en una sola columna llamada "value" y se utiliza "diagnosis" como identificador.
sns.boxplot(x="value", y="variable", hue="diagnosis", data=melted_data): Esta línea de código crea el gráfico de caja utilizando la función sns.boxplot. Aquí están los argumentos y opciones utilizados:
x="value": Los valores que se distribuirán en el gráfico de caja, que son las características derritidas en la columna "value."
y="variable": Las variables o características originales que se muestran en el eje vertical del gráfico de caja.
hue="diagnosis": Divide los datos por diagnóstico (benigno o maligno), lo que significa que se crearán dos cajas por cada característica, una para cada diagnóstico.
data=melted_data: Los datos que se utilizarán para crear el gráfico de caja, que son los datos derretidos.
melted_data = pd.melt(df, id_vars = "diagnosis",value_vars = ['mean radius', 'mean texture','mean perimeter'])
sns.set(font_scale=1.8)
plt.figure(figsize = (20,10))
sns.boxplot(x = "value", y = "variable", hue="diagnosis",data= melted_data);
melted_data = pd.melt(df, id_vars = "diagnosis",value_vars = ['radius error',
'texture error', 'perimeter error'])
sns.set(font_scale=1.8)
plt.figure(figsize = (20,10))
sns.boxplot(x = "value", y = "variable", hue="diagnosis",data= melted_data);
melted_data = pd.melt(df, id_vars = "diagnosis",value_vars = ['worst radius',
'worst texture', 'worst perimeter'])
sns.set(font_scale=1.8)
plt.figure(figsize = (20,10))
sns.boxplot(x = "value", y = "variable", hue="diagnosis",data= melted_data);
El código que proporcionaste utiliza Seaborn para crear una matriz de gráficos de dispersión (scatter plots) junto con histogramas en la diagonal para visualizar relaciones entre múltiples características del conjunto de datos. Estos gráficos se organizan en una matriz de pares, donde cada par de características se compara en función del diagnóstico ("M" para maligno y "B" para benigno). Aquí tienes una explicación paso a paso:
sns.pairplot(data=df[columns], hue="diagnosis", palette='viridis', corner=True, height=7.0, aspect=1.0): Esta línea de código crea la matriz de gráficos de dispersión utilizando la función sns.pairplot. Aquí están los argumentos y opciones utilizados:
data=df[columns]: Especifica los datos que se utilizarán en la matriz de gráficos de dispersión. Se seleccionan solo las columnas mencionadas en la lista columns.
hue="diagnosis": Divide los datos por diagnóstico (benigno o maligno), lo que significa que se mostrarán diferentes colores en función del diagnóstico.
palette='viridis': Establece el esquema de colores que se utilizará en los gráficos. En este caso, se utiliza el esquema de colores 'viridis'.
corner=True: Esta opción coloca histogramas en la diagonal principal de la matriz de gráficos de dispersión para mostrar la distribución de cada característica.
height=7.0: Establece la altura de los gráficos en la matriz.
aspect=1.0: Establece la relación de aspecto de los gráficos. En este caso, se mantiene la relación de aspecto predeterminada.
columns = ['diagnosis', 'mean radius', 'mean texture', 'mean perimeter', 'mean area', 'mean smoothness', 'mean compactness', 'mean concavity']
sns.set(font_scale=4.0)
sns.pairplot(data=df[columns], hue="diagnosis", palette='viridis', corner=True, height=7.0, aspect=1.0);
Diagrama de densidad de distribución KDE y distribución mediante el diagrama de dispersión stripplot()
El código que proporcionaste configura algunas opciones de estilo para Seaborn y luego crea una gráfica de densidad de kernel (kernel density estimate, KDE) utilizando FacetGrid. Aquí tienes una explicación paso a paso:
matplotlib.rc_file_defaults(): Esta línea de código restablece la configuración de estilo de Matplotlib a los valores predeterminados. Esto puede ser útil si deseas eliminar cualquier configuración personalizada previa que haya sido aplicada.
sns.set_style("ticks"): Establece el estilo de fondo y los ejes de Seaborn en "ticks". Esto significa que los ejes tendrán marcas de graduación en forma de "ticks" en lugar del estilo predeterminado "whitegrid" que incluye una cuadrícula.
sns.FacetGrid(df, hue="diagnosis", height=6): Aquí se crea un objeto FacetGrid de Seaborn. FacetGrid se utiliza para crear múltiples gráficos basados en una variable categórica (hue en este caso). El argumento hue="diagnosis" significa que se crearán gráficos para cada valor único en la columna "diagnosis" (que es "M" para maligno y "B" para benigno) y se diferenciarán por color.
.map(sns.kdeplot, "mean radius"): Se utiliza el método .map para mapear una gráfica de densidad de kernel (KDE) a los datos. El argumento "mean radius" especifica la columna de datos que se utilizará para el KDE.
.add_legend(): Esta función agrega una leyenda al gráfico para indicar qué color corresponde a cada valor único en la columna "diagnosis" (en este caso, "M" y "B")
matplotlib.rc_file_defaults()
sns.set_style("ticks")
sns.FacetGrid(df, hue="diagnosis", height=6).map(sns.kdeplot, "mean radius").add_legend();
sns.stripplot(x="diagnosis", y="mean radius", data=df, jitter=True, edgecolor="gray"): Esta línea de código crea el gráfico de dispersión utilizando la función sns.stripplot. Aquí están los argumentos y opciones utilizados:
x="diagnosis": Especifica que los valores de diagnóstico se mostrarán en el eje x (horizontal).
y="mean radius": Especifica que los valores de "mean radius" se mostrarán en el eje y (vertical).
data=df: Los datos que se utilizarán para crear el gráfico, que provienen del DataFrame df.
jitter=True: El parámetro jitter se utiliza para agregar un pequeño desplazamiento aleatorio a los puntos a lo largo del eje x. Esto se hace para evitar la superposición de puntos que tienen el mismo valor en el eje x y hacer que los puntos sean más visibles en el gráfico.
edgecolor="gray": Establece el color del borde de los puntos en el gráfico.
sns.stripplot(x="diagnosis", y="mean radius", data=df, jitter=True, edgecolor="gray");
La multicolinealidad es un problema ya que reduce la importancia de las variables independientes. A fin de solucionar este problem, se eliminan los predictores altamente correlacionados. Podemos comprobar la presencia de multicolinealidad entre algunas de las variables. Por ejemplo, la columna mean radius tiene una correlación de 1.0 con las columnas mean perimeter y mean area, respectivamente. Esto se debe a que las tres columnas contienen esencialmente la misma información, que es el tamaño físico de la observación.
Por lo tanto, sólo debemos elegir una de las tres columnas cuando pasemos al análisis posterior. Otro lugar donde la multicolinealidad es evidente, es entre las columnas "mean" y "worst". Por ejemplo, la columna mean radius tiene una correlación de 1.0 con la columna worst radius. También hay multicolinealidad entre los atributos compactness, concavity y concave points. Así que podemos elegir sólo uno de estos, por ejemplo compactness. De la matriz de correlación sabemos que estas columnas están altamente correlacionadas con las columnas mean radius, perimeter, area. Por lo tanto, estas columnas serán eliminadas.
inplace=True: Este argumento indica que la eliminación se realizará directamente en el DataFrame df en lugar de crear una copia modificada. Cuando inplace=True, el DataFrame se modifica en su lugar y no se devuelve un nuevo DataFrame.
cols = ['worst radius',
'worst texture',
'worst perimeter',
'worst area',
'worst smoothness',
'worst compactness',
'worst concavity',
'worst concave points',
'worst symmetry',
'worst fractal dimension']
df.drop(cols, inplace=True, axis=1);
cols = ['mean perimeter',
'mean area',
'perimeter error',
'area error']
df.drop(cols, inplace=True, axis=1);
cols = ['mean concavity',
'concavity error',
'mean concave points',
'concave points error']
df.drop(cols, inplace=True, axis=1);
df.columns
Index(['diagnosis', 'mean radius', 'mean texture', 'mean smoothness',
'mean compactness', 'mean symmetry', 'mean fractal dimension',
'radius error', 'texture error', 'smoothness error',
'compactness error', 'symmetry error', 'fractal dimension error'],
dtype='object')
corr = df.corr().round(2): Esta línea de código calcula la matriz de correlación entre las características del DataFrame df y redondea los valores a dos decimales. La matriz de correlación muestra cómo están relacionadas linealmente las características entre sí.
cmap = sns.diverging_palette(220, 10, as_cmap=True): Se crea un mapa de colores personalizado (cmap) utilizando la función sns.diverging_palette. Este mapa de colores va desde un color (azul en este caso) a otro color (naranja) y es útil para visualizar relaciones simétricas, ya que se considera un mapa de colores divergente.
mask = np.zeros_like(corr): Se crea una matriz de máscara (mask) de ceros con las mismas dimensiones que la matriz de correlación corr. Esta matriz se utilizará para ocultar la mitad superior de la matriz de correlación en el gráfico de calor.
mask[np.triu_indices_from(mask)] = True: Se establecen los valores en True en la matriz de máscara mask en la triangular superior de la matriz. Esto oculta la mitad superior de la matriz de correlación, ya que no contiene información nueva y evitará la redundancia en el gráfico de calor.
f, ax = plt.subplots(figsize=(20, 20)): Se crea una figura de Matplotlib con un tamaño de 20x20 pulgadas para contener el gráfico de calor.
sns.heatmap(corr, mask=mask, cmap=cmap, vmin=-1, vmax=1, center=0, square=True, linewidths=.5, cbar_kws={"shrink": .5}, annot=True): Se utiliza la función sns.heatmap para crear el mapa de calor. Aquí están los argumentos y opciones utilizados:
corr: La matriz de correlación que se va a visualizar. mask=mask: La matriz de máscara que oculta la triangular superior de la matriz de correlación. cmap=cmap: El mapa de colores personalizado creado anteriormente. vmin=-1 y vmax=1: Establecen los valores mínimo y máximo en el mapa de calor para que varíen de -1 a 1, ya que la correlación puede variar en ese rango. center=0: Establece el valor central en 0 en el mapa de calor. square=True: Hace que el mapa de calor tenga una relación de aspecto cuadrada. linewidths=.5: Establece el ancho de las líneas que separan las celdas en el mapa de calor. cbar_kws={"shrink": .5}: Personaliza la barra de color (barra de colores) en el gráfico de calor. annot=True: Muestra los valores de correlación en cada celda del mapa de calor. plt.tight_layout(): Ajusta automáticamente el diseño de la figura para que los elementos se ajusten correctamente.
corr = df.corr().round(2)
cmap = sns.diverging_palette(220, 10, as_cmap=True)
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
sns.set(font_scale=1.8)
f, ax = plt.subplots(figsize=(20, 20))
sns.heatmap(corr, mask=mask, cmap=cmap, vmin=-1, vmax=1, center=0, square=True, linewidths=.5,
cbar_kws={"shrink": .5}, annot=True)
plt.tight_layout()
División en entrenamiento (train) y prueba (test). Dividimos la variable objetivo y las variables independientes. A continuación, evaluamos el rendimiento del conjunto de entrenamiento y de prueba con diferentes números de vecinos
X = df.drop(['diagnosis'], axis = 1)
y = df['diagnosis']
from sklearn.neighbors import KNeighborsClassifier
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=cancer.target, random_state=66): Esta línea de código divide los datos en conjuntos de entrenamiento y prueba utilizando la función train_test_split de Scikit-Learn. Aquí están los argumentos utilizados:
X: Representa las características (variables independientes) de tus datos. y: Representa las etiquetas o valores objetivo (variable dependiente) que deseas predecir. stratify=cancer.target: Garantiza que las divisiones de entrenamiento y prueba mantengan la misma proporción de clases que la variable objetivo cancer.target. Esto es importante para problemas de clasificación, especialmente si hay desequilibrios en las clases. random_state=66: Establece una semilla aleatoria para garantizar que la división de los datos sea reproducible. training_accuracy = [] y test_accuracy = []: Estas listas vacías se utilizan para almacenar las puntuaciones de precisión (accuracy) en el conjunto de entrenamiento y prueba para diferentes valores de n_neighbors.
neighbors_settings = range(1, 15): Esta línea de código crea una secuencia de valores n_neighbors desde 1 hasta 14 (inclusive). Estos valores se utilizarán para ajustar el hiperparámetro n_neighbors en el clasificador k-NN.
Bucle for n_neighbors in neighbors_settings:: Este bucle itera a través de los diferentes valores de n_neighbors que se han definido previamente.
clf = KNeighborsClassifier(n_neighbors=n_neighbors): Se crea un clasificador k-NN con el valor actual de n_neighbors. Cada iteración del bucle utiliza un valor diferente de n_neighbors.
clf.fit(X_train, y_train): Se ajusta el modelo k-NN al conjunto de entrenamiento.
training_accuracy.append(clf.score(X_train, y_train)): Se calcula y se almacena la precisión del modelo en el conjunto de entrenamiento utilizando clf.score(), y el resultado se agrega a la lista training_accuracy.
test_accuracy.append(clf.score(X_test, y_test)): Se calcula y se almacena la precisión del modelo en el conjunto de prueba utilizando clf.score(), y el resultado se agrega a la lista test_accuracy
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=cancer.target, random_state=66)
training_accuracy = []
test_accuracy = []
neighbors_settings = range(1, 15)
for n_neighbors in neighbors_settings:
clf = KNeighborsClassifier(n_neighbors=n_neighbors)
clf.fit(X_train, y_train)
training_accuracy.append(clf.score(X_train, y_train))
test_accuracy.append(clf.score(X_test, y_test))
Cuando llamas a matplotlib.rc_file_defaults(), estás deshaciendo cualquier configuración personalizada que hayas realizado anteriormente en tu sesión de Python y devolviendo Matplotlib a su estado inicial con los valores predeterminados. Esto puede ser útil si has realizado cambios en la configuración de Matplotlib y deseas volver a la configuración original.
matplotlib.rc_file_defaults()
plt.plot(neighbors_settings, training_accuracy, label="Training accuracy")
plt.plot(neighbors_settings, test_accuracy, label="Test accuracy")
plt.ylabel("Accuracy")
plt.xlabel("n_neighbors")
plt.legend();
El gráfico muestra accuracy para los conjuntos de entrenamiento y de prueba en el eje contra el ajuste de n_vecinos en el eje . Aunque los gráficos del mundo real no suelen ser muy suaves, podemos reconocer algunas de las características del sobreajuste (overfitting) y del subajuste (underfitting). Si se considera un solo vecino más cercano, la predicción en el conjunto de entrenamiento es perfecta. Pero cuando se consideran más vecinos, el modelo se simplifica y la precisión del entrenamiento disminuye.
La precisión del conjunto de prueba cuando se utiliza un solo vecino es menor que cuando se utilizan más vecinos, lo que indica que el uso de un solo vecino más cercano conduce a una mayor precisión en el conjunto de entrenamiento (modelo demasiado complejo). Pero cuando se consideran más vecinos, el modelo se simplifica y la precisión del entrenamiento disminuye.
clf = KNeighborsClassifier(n_neighbors=9)
clf.fit(X_train, y_train)
print(clf.score(X_train, y_train), clf.score(X_test, y_test))
0.8990610328638498 0.9020979020979021
Regresión por k-vecinos¶
Cuando se utilizan varios vecinos más cercanos, la predicción es el promedio, o la media, de los vecinos
El algoritmo de k-vecinos más cercanos para la regresión se implementa en la clase KNeighbors Regressor en scikit-learn. Se utiliza de forma similar a KNeighborsClassifier
mglearn.plots.plot_knn_regression(n_neighbors=3)
mglearn.plots.plot_knn_regression(n_neighbors=1)
from sklearn.neighbors import KNeighborsRegressor
X, y = mglearn.datasets.make_wave(n_samples=40)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_train, y_train)
KNeighborsRegressor(n_neighbors=3)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
KNeighborsRegressor(n_neighbors=3)
Ahora podemos hacer predicciones sobre el conjunto de prueba:
print("Test set predictions:\n{}".format(reg.predict(X_test)))
Test set predictions: [-0.05396539 0.35686046 1.13671923 -1.89415682 -1.13881398 -1.63113382 0.35686046 0.91241374 -0.44680446 -1.13881398]
También podemos evaluar el modelo utilizando el método score, que para los regresores devuelve la puntuación R^2. La puntuación R^2 , también conocida como coeficiente de determinación, es una medida de predicción de un modelo de regresión, y arroja una puntuación entre 0 y 1. Un valor de 1 corresponde a una predicción perfecta, y un valor de 0 corresponde a un modelo constante que sólo predice la media de las respuestas del conjunto de entrenamiento, y_train. Aquí, el score es de 0.83, lo que indica un ajuste del modelo relativamente bueno
print("Test set R^2: {:.2f}".format(reg.score(X_test, y_test)))
Test set R^2: 0.83
Análisis de KNeighborsRegressor¶
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
line = np.linspace(-3, 3, 1000).reshape(-1, 1)
for n_neighbors, ax in zip([1, 3, 9], axes):
reg = KNeighborsRegressor(n_neighbors=n_neighbors)
reg.fit(X_train, y_train)
ax.plot(line, reg.predict(line))
ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8)
ax.plot(X_test, y_test, 'v', c=mglearn.cm2(1), markersize=8)
ax.set_title("{} neighbor(s)\n train score: {:.2f} test score: {:.2f}".format(n_neighbors,
reg.score(X_train, y_train),
reg.score(X_test, y_test)))
ax.set_xlabel("Feature")
ax.set_ylabel("Target")
axes[0].legend(["Model predictions", "Training data/target", "Test data/target"], loc="best");
Tener en cuenta más vecinos conduce a predicciones más suaves, pero éstas no se ajustan tan bien a los datos de entrenamiento
Aplicación: World Hydropower Generation¶
El conjunto de datos utilizado es una recopilación de la generación de energía de varios países europeos, medida en THh entre 2000 y 2019. Su contenido fue extraído de World in Data
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn import neighbors
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_squared_log_error
from sklearn.metrics import r2_score
from sklearn.metrics import explained_variance_score
Para medir la eficacia de nuestros modelos generados, utilizamoslas siguientes métricas:
mean_absolute_error (MAE): medida de los errores entre observaciones emparejadas que expresan el mismo fenómeno;
mean_squared_error (root - RMSE): la desviación estándar de los residuos (errores de predicción);
mean_squared_log_error (root - RMSLE): mide la relación entre lo real y lo predicho;
r2_score (R2):: coeficiente de determinación, la proporción de la varianza en la variable dependiente que es predecible a partir de la(s) variable(s) independiente(s); y
explained_variance_score (EVS): mide la discrepancia entre un modelo y los datos reales.
pd.options.display.float_format = '{:.4f}'.format
scaler = MinMaxScaler(feature_range=(0, 1))
df_power = pd.read_csv('https://raw.githubusercontent.com/lihkir/Data/main/Hydropower_Consumption.csv', sep=',')
df_power.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 153 entries, 0 to 152 Data columns (total 21 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Country 153 non-null object 1 2000 153 non-null int64 2 2001 153 non-null int64 3 2002 153 non-null int64 4 2003 153 non-null int64 5 2004 153 non-null int64 6 2005 153 non-null int64 7 2006 153 non-null int64 8 2007 153 non-null int64 9 2008 153 non-null int64 10 2009 153 non-null int64 11 2010 153 non-null int64 12 2011 153 non-null int64 13 2012 153 non-null int64 14 2013 153 non-null int64 15 2014 153 non-null int64 16 2015 153 non-null int64 17 2016 153 non-null int64 18 2017 153 non-null int64 19 2018 153 non-null int64 20 2019 153 non-null int64 dtypes: int64(20), object(1) memory usage: 25.2+ KB
df_power.head()
| Country | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | ... | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Afghanistan | 312 | 498 | 555 | 63 | 565 | 59 | 637 | 748 | 542 | ... | 751 | 595 | 71 | 804 | 895 | 989 | 1025 | 105 | 105 | 107 |
| 1 | Africa | 75246 | 80864 | 85181 | 82873 | 87405 | 89066 | 92241 | 95341 | 97157 | ... | 107427 | 110445 | 110952 | 117673 | 123727 | 115801 | 123816 | 130388 | 132735 | 0 |
| 2 | Albania | 4548 | 3519 | 3477 | 5117 | 5411 | 5319 | 4951 | 276 | 3759 | ... | 7673 | 4036 | 4725 | 6959 | 4726 | 5866 | 7136 | 448 | 448 | 4018 |
| 3 | Algeria | 54 | 69 | 57 | 265 | 251 | 555 | 218 | 226 | 283 | ... | 173 | 378 | 389 | 99 | 193 | 145 | 72 | 56 | 117 | 152 |
| 4 | Angola | 903 | 1007 | 1132 | 1229 | 1733 | 2197 | 2638 | 2472 | 3103 | ... | 3666 | 3967 | 3734 | 4719 | 4991 | 5037 | 5757 | 7576 | 7576 | 8422 |
5 rows × 21 columns
countries = df_power.Country
df_power = df_power.drop(columns = ["Country"])
df_power = pd.DataFrame(scaler.fit_transform(df_power),
columns=['2000','2001','2002','2003','2004','2005',
'2006','2007','2008','2009','2010','2011',
'2012','2013','2014','2015','2016','2017',
'2018','2019'])
df_power.describe()
| 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 | 153.0000 |
| mean | 0.0327 | 0.0353 | 0.0355 | 0.0328 | 0.0419 | 0.0390 | 0.0415 | 0.0434 | 0.0371 | 0.0445 | 0.0328 | 0.0433 | 0.0352 | 0.0290 | 0.0282 | 0.0265 | 0.0306 | 0.0296 | 0.0388 | 0.0259 |
| std | 0.1229 | 0.1262 | 0.1278 | 0.1249 | 0.1372 | 0.1364 | 0.1366 | 0.1440 | 0.1293 | 0.1493 | 0.1208 | 0.1459 | 0.1294 | 0.1121 | 0.1062 | 0.1031 | 0.1117 | 0.1110 | 0.1317 | 0.1043 |
| min | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 |
| 25% | 0.0002 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0004 | 0.0003 | 0.0004 | 0.0004 | 0.0003 | 0.0004 | 0.0002 | 0.0004 | 0.0002 | 0.0003 | 0.0002 | 0.0002 | 0.0002 | 0.0003 | 0.0002 |
| 50% | 0.0027 | 0.0030 | 0.0026 | 0.0027 | 0.0037 | 0.0028 | 0.0031 | 0.0039 | 0.0032 | 0.0035 | 0.0043 | 0.0040 | 0.0034 | 0.0025 | 0.0024 | 0.0019 | 0.0025 | 0.0020 | 0.0029 | 0.0019 |
| 75% | 0.0116 | 0.0128 | 0.0119 | 0.0118 | 0.0155 | 0.0107 | 0.0149 | 0.0151 | 0.0147 | 0.0152 | 0.0145 | 0.0156 | 0.0140 | 0.0096 | 0.0121 | 0.0086 | 0.0117 | 0.0113 | 0.0171 | 0.0097 |
| max | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 |
Ahora, el objetivo es crear un modelo que prediga la generación de energía para 2019, basándose en los 18 años anteriores (2000 - 2018) con al menos un 75% de precisión. Para ello, el conjunto de datos se separó en X e y, siendo X datos de predicción e y lo que se pretende predecir. Para ello, se dividen en entrenamiento (70%) y prueba (30%).
X = df_power.drop(columns = ["2019"])
y = df_power["2019"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
Como ya tenemos seleccionados los datos de entrenamiento y de prueba, es necesario encontrar el factor k que genere los mejores resultados para el algoritmo. Una de las formas de encontrar este factor k es realizar una prueba con varios valores y medir los resultados porcentuales. Será necesario agotar un gran número de posibilidades de k
rmsle_val = []
best_rmsle = 1.0
for k in range(20):
k = k+1
knn = neighbors.KNeighborsRegressor(n_neighbors = k)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
rmsle = np.sqrt(mean_squared_log_error(y_test,y_pred))
if (rmsle < best_rmsle):
best_rmsle = rmsle
best_k = k
rmsle_val.append(rmsle)
print('RMSLE value for k= ' , k , 'is:', rmsle)
print(f"Best RMSLE: {best_rmsle}, Best k: {best_k}")
RMSLE value for k= 1 is: 0.05771085849033799 RMSLE value for k= 2 is: 0.046287824938614094 RMSLE value for k= 3 is: 0.05245290672301628 RMSLE value for k= 4 is: 0.03904370874530979 RMSLE value for k= 5 is: 0.04413545109970342 RMSLE value for k= 6 is: 0.049397885066996064 RMSLE value for k= 7 is: 0.05368842644105438 RMSLE value for k= 8 is: 0.057205532019402504 RMSLE value for k= 9 is: 0.05970971610365409 RMSLE value for k= 10 is: 0.06264220706736034 RMSLE value for k= 11 is: 0.06519555508343818 RMSLE value for k= 12 is: 0.06683376633168203 RMSLE value for k= 13 is: 0.06835697276199447 RMSLE value for k= 14 is: 0.0700987590270357 RMSLE value for k= 15 is: 0.0714815908074978 RMSLE value for k= 16 is: 0.07276108145829546 RMSLE value for k= 17 is: 0.07381745094856672 RMSLE value for k= 18 is: 0.07488933459172763 RMSLE value for k= 19 is: 0.07579673979403485 RMSLE value for k= 20 is: 0.07667076282857414 Best RMSLE: 0.03904370874530979, Best k: 4
Nuestra métrica indica que el menor error se produce cuando tenemos k=4 (RMSLE de 0.0390), lo que indica un error relativo entre los valores predichos y los actuales del 3,90%. Por lo tanto, presentaremos todos los valores en un gráfico, que nos mostrará visualmente los resultados obtenidos. Esta función se conoce como "función codo", dada la variación porcentual que se produce entre los valores de k, primero hacia abajo y luego hacia arriba, cuando k encuentra su mejor valor. Estamos trazando los valores RMSLE frente a los valores de k .
curve = pd.DataFrame(rmsle_val)
curve.plot(figsize=(8,5));
También haremos uso de la función de score, que nos permitirá ver la tasa de precisión de nuestro modelo (80,16%).
knn = neighbors.KNeighborsRegressor(n_neighbors = 4)
knn.fit(X_train,y_train)
y_pred = knn.predict(X_test)
knn.score(X_test, y_test)
0.8016285342188538
Una vez se ha construido el modelo y se prueban algunas predicciones, podemos aplicar las métricas y analizar los resultados. Aquí se comparan el conjunto de prueba con las predicciones usando las métricas R2, EVS, MAE, RMSE, RMSLE, para verificar cuáles son los resultados de cada una de ellas.
r2_valid = r2_score(y_test, y_pred)
mae_valid = mean_absolute_error(y_test, y_pred)
evs_valid = explained_variance_score(y_test, y_pred, multioutput='uniform_average')
rmse_valid = np.sqrt(mean_squared_error(y_test, y_pred))
rmsle_valid = np.sqrt(mean_squared_log_error(y_test, y_pred))
print('R2 Valid:',r2_valid)
print('EVS Valid:', evs_valid)
print('MAE Valid:', mae_valid)
print('RMSE Valid:',rmse_valid)
print('RMSLE Valid:', rmsle_valid)
R2 Valid: 0.8016285342188538 EVS Valid: 0.8131526313306373 MAE Valid: 0.017960057840249434 RMSE Valid: 0.04999229884993785 RMSLE Valid: 0.03904370874530979
Una vez realizada la predicción y comprobado el modelo, organizamos los resultados uno al lado del otro para poder hacer una comparación.
country_test = countries[len(countries)-len(y_test):]
data_prediction = list(zip(y_test,y_pred))
data_prediction = pd.DataFrame(data_prediction, columns=['Test','Prediction'])
data_prediction = data_prediction.set_index(country_test)
data_prediction.head(10)
| Test | Prediction | |
|---|---|---|
| Country | ||
| Papua New Guinea | 0.0406 | 0.0268 |
| Paraguay | 0.0044 | 0.0039 |
| Peru | 0.0134 | 0.0096 |
| Phillipines | 0.0280 | 0.0128 |
| Poland | 0.0164 | 0.0125 |
| Portugal | 0.0321 | 0.0155 |
| Puerto Rico | 0.4982 | 0.3417 |
| Reunion | 0.5331 | 0.3417 |
| Romania | 0.0007 | 0.0075 |
| Russia | 0.0032 | 0.0039 |
plt.xticks(rotation=90): Establece la rotación de las etiquetas en el eje x a 90 grados para que los nombres de los países sean legibles en el gráfico, especialmente si son largos.
plt.legend(loc="upper right"): Agrega una leyenda al gráfico en la esquina superior derecha para distinguir las líneas "Test" y "Prediction".
plt.rcParams.update({'font.size': 18});
plt.figure(figsize=(20, 8));
plt.plot(data_prediction.index, data_prediction.Test, label="Test");
plt.plot(data_prediction.index, data_prediction.Prediction, label="Prediction");
plt.xticks(rotation=90);
plt.legend(loc="upper right");
plt.xlabel("Country");
plt.ylabel("Hydropower Generation");
Modelos lineales¶
Los modelos lineales hacen una predicción utilizando una función lineal de las características de entrada
EDA Análisis exploratorio de datos¶
El EDA puede utilizarse para buscar valores atípicos, patrones y tendencias en los datos.
EDA ayuda a encontrar patrones significativos en los datos.
EDA proporciona una visión en profundidad de los conjuntos de datos para resolver nuestros problemas de negocio.
EDA proporciona una pista para imputar los valores que faltan en el conjunto de datos.
Asimetría
skew = 0: Distribución simétrica (valores aceptables skew $\in$ (-1,1)).
skew > 0: Mayor peso en la cola izquierda de la distribución (sesgo positivo).
skew < 0: Mayor peso en la cola derecha de la distribución (sesgo negativo).
Kurtosis: Determina si una distribución tiene colas gruesas con respecto a la distribución normal. Proporciona información sobre la forma de una distribución de frecuencias.
kurtosis=3: se denomina mesocúrtica (distribución normal).
kurtosis<3: se denomina platicúrtica (distribución con colas menos gruesas que la normal).
kurtosis>3: se denomina leptocúrtica (distribución con colas más gruesas que la normal) y significa que trata de producir más valores atípicos que la distribución normal.
data = pd.read_csv('https://raw.githubusercontent.com/lihkir/Data/main/boston.csv', index_col=0)
data.head()
| crim | zn | indus | chas | nox | rm | age | dis | rad | tax | ptratio | black | lstat | medv | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0.0063 | 18.0000 | 2.3100 | 0 | 0.5380 | 6.5750 | 65.2000 | 4.0900 | 1 | 296 | 15.3000 | 396.9000 | 4.9800 | 24.0000 |
| 2 | 0.0273 | 0.0000 | 7.0700 | 0 | 0.4690 | 6.4210 | 78.9000 | 4.9671 | 2 | 242 | 17.8000 | 396.9000 | 9.1400 | 21.6000 |
| 3 | 0.0273 | 0.0000 | 7.0700 | 0 | 0.4690 | 7.1850 | 61.1000 | 4.9671 | 2 | 242 | 17.8000 | 392.8300 | 4.0300 | 34.7000 |
| 4 | 0.0324 | 0.0000 | 2.1800 | 0 | 0.4580 | 6.9980 | 45.8000 | 6.0622 | 3 | 222 | 18.7000 | 394.6300 | 2.9400 | 33.4000 |
| 5 | 0.0691 | 0.0000 | 2.1800 | 0 | 0.4580 | 7.1470 | 54.2000 | 6.0622 | 3 | 222 | 18.7000 | 396.9000 | 5.3300 | 36.2000 |
data.info()
<class 'pandas.core.frame.DataFrame'> Index: 506 entries, 1 to 506 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 crim 506 non-null float64 1 zn 506 non-null float64 2 indus 506 non-null float64 3 chas 506 non-null int64 4 nox 506 non-null float64 5 rm 506 non-null float64 6 age 506 non-null float64 7 dis 506 non-null float64 8 rad 506 non-null int64 9 tax 506 non-null int64 10 ptratio 506 non-null float64 11 black 506 non-null float64 12 lstat 506 non-null float64 13 medv 506 non-null float64 dtypes: float64(11), int64(3) memory usage: 59.3 KB
Información de atributos (por orden):
CRIM: tasa de criminalidad per cápita por ciudad
ZN: proporción de suelo residencial para parcelas de más de 25.000 pies cuadrados
INDUS: proporción de acres comerciales no minoristas por ciudad
CHAS: Variable dummy del Río Charles (= 1 si el tramo limita con el río; 0 en caso contrario)
NOX: concentración de óxidos nítricos (partes por 10 millones)
RM: número medio de habitaciones por vivienda
AGE: proporción de unidades ocupadas por sus propietarios construidas antes de 1940
DIS: distancias ponderadas a cinco centros de empleo de Boston
RAD: índice de accesibilidad a autopistas radiales
TAX: tipo del impuesto sobre bienes inmuebles de valor íntegro por 10.000 dólares
PTRATIO: relación alumnos-profesor por ciudad
B: 1000(Bk - 0,63)^2 donde Bk es la proporción de negros por ciudad
LSTAT: % más bajo de la población
MEDV: Valor medio de las viviendas ocupadas por sus propietarios en $1000’s.
La función nunique() se utiliza para calcular el número de valores únicos en cada columna de un DataFrame de Pandas. Si aplicas esta función a un DataFrame llamado data, proporcionará la cantidad de valores únicos en cada columna de ese DataFrame.
data.nunique()
crim 504 zn 26 indus 76 chas 2 nox 81 rm 446 age 356 dis 412 rad 9 tax 66 ptratio 46 black 357 lstat 455 medv 229 dtype: int64
data.isnull().sum()
crim 0 zn 0 indus 0 chas 0 nox 0 rm 0 age 0 dis 0 rad 0 tax 0 ptratio 0 black 0 lstat 0 medv 0 dtype: int64
(data.isnull().sum()/(len(data)))*100
crim 0.0000 zn 0.0000 indus 0.0000 chas 0.0000 nox 0.0000 rm 0.0000 age 0.0000 dis 0.0000 rad 0.0000 tax 0.0000 ptratio 0.0000 black 0.0000 lstat 0.0000 medv 0.0000 dtype: float64
data['chas'] = data['chas'].astype(object)
los estadísticos descriptivos generados por describe() se centrarán en las columnas numéricas, y la variable de tipo "objeto" no se incluirá en la salida.
data.describe().T
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| crim | 506.0000 | 3.6135 | 8.6015 | 0.0063 | 0.0820 | 0.2565 | 3.6771 | 88.9762 |
| zn | 506.0000 | 11.3636 | 23.3225 | 0.0000 | 0.0000 | 0.0000 | 12.5000 | 100.0000 |
| indus | 506.0000 | 11.1368 | 6.8604 | 0.4600 | 5.1900 | 9.6900 | 18.1000 | 27.7400 |
| nox | 506.0000 | 0.5547 | 0.1159 | 0.3850 | 0.4490 | 0.5380 | 0.6240 | 0.8710 |
| rm | 506.0000 | 6.2846 | 0.7026 | 3.5610 | 5.8855 | 6.2085 | 6.6235 | 8.7800 |
| age | 506.0000 | 68.5749 | 28.1489 | 2.9000 | 45.0250 | 77.5000 | 94.0750 | 100.0000 |
| dis | 506.0000 | 3.7950 | 2.1057 | 1.1296 | 2.1002 | 3.2074 | 5.1884 | 12.1265 |
| rad | 506.0000 | 9.5494 | 8.7073 | 1.0000 | 4.0000 | 5.0000 | 24.0000 | 24.0000 |
| tax | 506.0000 | 408.2372 | 168.5371 | 187.0000 | 279.0000 | 330.0000 | 666.0000 | 711.0000 |
| ptratio | 506.0000 | 18.4555 | 2.1649 | 12.6000 | 17.4000 | 19.0500 | 20.2000 | 22.0000 |
| black | 506.0000 | 356.6740 | 91.2949 | 0.3200 | 375.3775 | 391.4400 | 396.2250 | 396.9000 |
| lstat | 506.0000 | 12.6531 | 7.1411 | 1.7300 | 6.9500 | 11.3600 | 16.9550 | 37.9700 |
| medv | 506.0000 | 22.5328 | 9.1971 | 5.0000 | 17.0250 | 21.2000 | 25.0000 | 50.0000 |
num_cols = data.select_dtypes(include=np.number).columns.tolist(): En esta línea, se utiliza el método select_dtypes nuevamente para seleccionar todas las columnas del DataFrame data que tienen un tipo de dato numérico. Esto se hace utilizando np.number como argumento para include. Luego, se utiliza columns para obtener los nombres de estas columnas numéricas y tolist() se usa comúnmente cuando necesitas convertir un objeto iterable, como una Serie de Pandas, en una lista de Python.
cat_cols=data.select_dtypes(include=['object']).columns
num_cols = data.select_dtypes(include=np.number).columns.tolist()
print("Categorical Variables:")
print(cat_cols)
print("Numerical Variables:")
print(num_cols)
Categorical Variables: Index(['chas'], dtype='object') Numerical Variables: ['crim', 'zn', 'indus', 'nox', 'rm', 'age', 'dis', 'rad', 'tax', 'ptratio', 'black', 'lstat', 'medv']
Variables numéricas:¶
plt.figure(figsize=(14, 6)): Se crea una figura de Matplotlib para los gráficos. Los gráficos se mostrarán uno al lado del otro en la misma figura.
plt.subplot(1, 2, 1): Se crea el primer subgráfico en una cuadrícula de 1 fila y 2 columnas. Este será el histograma de la columna numérica.
data[col].hist(grid=False): Se crea un histograma de la columna numérica y se muestra en el primer subgráfico.
plt.subplot(1, 2, 2): Se crea el segundo subgráfico en la misma cuadrícula. Este será un gráfico de caja (boxplot) de la columna numérica.
sns.boxplot(x=data[col]): Se crea un gráfico de caja de la columna numérica y se muestra en el segundo subgráfico.
plt.show(): Se muestra la figura con los dos subgráficos.
En resumen, este código realiza un análisis exploratorio de las columnas numéricas en tu DataFrame. Para cada columna, muestra el sesgo, la curtosis y crea un histograma y un gráfico de caja para visualizar la distribución de los datos. Esto puede ser útil para comprender la forma y la dispersión de tus datos numéricos.
custom_palette = sns.color_palette("Set2")
sns.set(font_scale=1.4)
for col in num_cols:
print('Column: ', col)
print('Skew:', round(data[col].skew(), 2))
print('Kurtosis: ', round(data[col].kurtosis(), 2))
plt.figure(figsize = (9, 3))
plt.subplot(1, 2, 1)
data[col].hist(grid=False, color=custom_palette[2]) #sin fondo cuadriculado y primer color de los colores personalizados
plt.subplot(1, 2, 2)
sns.boxplot(x=data[col], palette='Set2')
plt.show()
Column: crim Skew: 5.22 Kurtosis: 37.13
Column: zn Skew: 2.23 Kurtosis: 4.03
Column: indus Skew: 0.3 Kurtosis: -1.23
Column: nox Skew: 0.73 Kurtosis: -0.06
Column: rm Skew: 0.4 Kurtosis: 1.89
Column: age Skew: -0.6 Kurtosis: -0.97
Column: dis Skew: 1.01 Kurtosis: 0.49
Column: rad Skew: 1.0 Kurtosis: -0.87
Column: tax Skew: 0.67 Kurtosis: -1.14
Column: ptratio Skew: -0.8 Kurtosis: -0.29
Column: black Skew: -2.89 Kurtosis: 7.23
Column: lstat Skew: 0.91 Kurtosis: 0.49
Column: medv Skew: 1.11 Kurtosis: 1.5
Variables categóricas: Usando diagramas de barras representamos la variable dummy del Río Charles (= 1 si el tramo limita con el río; 0 en caso contrario)
matplotlib.rc_file_defaults()
data['chas'].value_counts(): Esta parte cuenta la frecuencia de cada valor único en la columna 'chas' de tu DataFrame 'data'. Esto significa que cuenta cuántas veces aparece cada categoría única en la columna 'chas'.
.index: Después de contar las frecuencias, value_counts() devuelve una Serie de Pandas que tiene como índice las categorías únicas (valores únicos) de la columna 'chas' y como valores las frecuencias de esas categorías.
order=data['chas'].value_counts().index: Finalmente, esta parte se utiliza para especificar el orden en el que se deben mostrar las categorías en el eje x del gráfico countplot. Se utiliza el índice de la Serie devuelta por value_counts() para determinar el orden. Esto significa que las categorías se mostrarán en el orden en el que aparecen en el índice, que generalmente es en función de su frecuencia, comenzando por la categoría más frecuente.
sns.countplot(x = 'chas', data = data, color =custom_palette[4] , order = data['chas'].value_counts().index);
Transformación de datos: Las variables por ejemplo crime y black, por ejemplo, están muy sesgadas y en una escala mayor. Hagamos una transformación logarítmica. La transformación logarítmica puede ayudar en la normalización, por lo que esta variable puede mantener la escala estándar con otras variables
def log_transform(data,col):
for colname in col:
if (data[colname] == 1.0).all():
data[colname + '_log'] = np.log(data[colname]+1)
else:
data[colname + '_log'] = np.log(data[colname])
data.info()
log_transform(data,['crim','black'])
<class 'pandas.core.frame.DataFrame'> Index: 506 entries, 1 to 506 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 crim 506 non-null float64 1 zn 506 non-null float64 2 indus 506 non-null float64 3 chas 506 non-null object 4 nox 506 non-null float64 5 rm 506 non-null float64 6 age 506 non-null float64 7 dis 506 non-null float64 8 rad 506 non-null int64 9 tax 506 non-null int64 10 ptratio 506 non-null float64 11 black 506 non-null float64 12 lstat 506 non-null float64 13 medv 506 non-null float64 14 crim_log 506 non-null float64 15 black_log 506 non-null float64 dtypes: float64(13), int64(2), object(1) memory usage: 67.2+ KB
sns.distplot(data["crim_log"], axlabel="crim_log");
sns.distplot(data["black_log"], axlabel="black_log");
Análisis bivariado: Pasemos ahora al análisis bivariado. El análisis bivariado ayuda a comprender cómo se relacionan las variables entre sí y la relación entre las variables dependientes e independientes presentes en el conjunto de datos. Puede utilizar el siguiente comando para visualizar todos los scatter plots, para las posibles relaciones
sns.pairplot(data=data.drop(['chas', 'black_log', 'crim_log'],axis=1));
def scatter_regplot(data, strx, stry):
sns.set(font_scale=1.4)
fig, ax = plt.subplots(1, 2, figsize=(14, 6), sharey=True)
sns.scatterplot(data=data, x=strx, y=stry, ax=ax[0])
sns.regplot(data=data, x=strx, y=stry, ax=ax[1]);
fig.suptitle('Relación entre %s y medv'%col)
num_cols.remove('medv')
for col in num_cols:
scatter_regplot(data, col, 'medv')
Un mapa de calor se utiliza ampliamente para este tipo de análisis. El mapa de calor muestra la correlación entre las variables, ya sea positiva o negativa
annot=True: Esto indica que se deben mostrar los valores de correlación en el mapa de calor.
vmin=-1 y vmax=1: Estos argumentos establecen los valores mínimo y máximo en la escala de colores para el mapa de calor, lo que garantiza que los valores de correlación estén en el rango de -1 a 1.
matplotlib.rc_file_defaults()
sns.set(font_scale=1.2)
plt.figure(figsize=(12, 7))
sns.heatmap(data.drop(['chas', 'black_log', 'crim_log'],axis=1).corr(), annot = True, vmin = -1, vmax = 1);
EJEMPLO:¶
A manera de ejemplo, utilizaremos el dataset mglearn.datasets.load_extended_boston() de las 104 características resultantes de las 13 características originales junto con las 91 combinaciones posibles de dos características dentro de esas 13
X, y = mglearn.datasets.load_extended_boston()
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
lr = LinearRegression().fit(X_train, y_train)
Al comparar los score de los conjuntos de entrenamiento y de prueba, comprobamos que predecimos con mucha precisión en el conjunto de entrenamiento, pero el R^2 en el conjunto de prueba es mucho peor
print("Training set score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lr.score(X_test, y_test)))
Training set score: 0.95 Test set score: 0.61
Esta discrepancia entre el rendimiento en el conjunto de entrenamiento y el conjunto de prueba es un claro signo de overfitting, y por lo tanto, debemos tratar de encontrar un modelo que nos permita controlar la complejidad. Usualmente, en este tipo de casos utilizamos técnicas de regularización. Una de las alternativas más utilizadas a la regresión lineal estándar es la regresión ridge, que estudiaremos a continuación.
REGRESIÓN RIDGE¶
La regresión ridge se implementa en linear_model.Ridge. Veamos qué tal lo hace en el conjunto de datos ampliado de Boston Housing.
ridge = Ridge().fit(X_train, y_train)
print("Training set score: {:.2f}".format(ridge.score(X_train, y_train)))
print("Test set score: {:.2f}".format(ridge.score(X_test, y_test)))
Training set score: 0.89 Test set score: 0.75
Como puede ver, el score en el conjunto de entrenamiento de Ridge es menor que el de la regresión lineal, cuyos puntajes fueron: Training set score: 0.95 and Test set score: 0.61. Además, la puntuación en el conjunto de prueba es mayor. En este caso, Ridge, usa alpha=1.0 como parámetro por default
Ridge es un modelo más restringido, por lo que existe menos probabilidad de overfitting. Un modelo menos complejo significa un peor rendimiento en el conjunto de de entrenamiento, pero uno mejor generalización. Como sólo nos interesa el rendimiento de la generalización, deberíamos elegir el modelo Ridge en lugar del modelo de regresión lineal.
El modelo Ridge hace un balance entre la simplicidad del modelo (coeficientes casi nulos) y su rendimiento en el conjunto de entrenamiento. La importancia que el modelo da a la simplicidad frente al rendimiento del conjunto de entrenamiento, puede ser especificada por el usuario, utilizando el parámetro alpha
Aumentar alpha obliga a los coeficientes a acercarse más a cero, lo que disminuye el rendimiento del conjunto de entrenamiento, pero puede ayudar a la generalización
ridge10 = Ridge(alpha=10).fit(X_train, y_train)
print("Training set score: {:.2f}".format(ridge10.score(X_train, y_train)))
print("Test set score: {:.2f}".format(ridge10.score(X_test, y_test)))
Training set score: 0.79 Test set score: 0.64
La disminución de alpha permite que los coeficientes estén menos restringidos. Para valores muy pequeños de alpha, los coeficientes apenas están restringidos, y terminamos con un modelo que se parece a LinearRegression
ridge01 = Ridge(alpha=0.1).fit(X_train, y_train)
print("Training set score: {:.2f}".format(ridge01.score(X_train, y_train)))
print("Test set score: {:.2f}".format(ridge01.score(X_test, y_test)))
Training set score: 0.93 Test set score: 0.77
Como era de esperarse, la puntuación de entrenamiento es mayor que la de prueba para todos los tamaños de conjuntos de datos, tanto para la regresión lineal como para la ridge. Debido a que la regresión ridge está regularizada, la puntuación de entrenamiento es inferior a la de la regresión lineal en todos los casos. Sin embargo, la puntuación de la prueba de la regresión ridge es mejor, en particular, para los subconjuntos pequeños de datos. Para menos de 400 puntos de datos, la regresión lineal no es capaz de aprender nada. A medida que el modelo dispone de más datos, ambos modelos mejoran, y la regresión lineal alcanza a la ridge.
mglearn.plots.plot_ridge_n_samples()
LASSO¶
Una alternativa a la regresión ridge para regularizar la regresión lineal es la regresión lasso. Al igual que con la regresión ridge, el uso de lasso también restringe los coeficientes para que sean cercanos a cero, pero de una forma ligeramente diferente, llamada regularización L1.
Apliquemos lasso al conjunto de datos ampliado de Boston Housing
lasso = Lasso().fit(X_train, y_train)
print("Training set score: {:.2f}".format(lasso.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
print("Number of features used: {}".format(np.sum(lasso.coef_ != 0)))
Training set score: 0.29 Test set score: 0.21 Number of features used: 4
Como se puede ver, Lasso lo hace bastante mal, tanto en el conjunto de entrenamiento como en el de prueba. Esto indica underfitting, pero, nótese que sólo utilizó 4 de las 105 características (feature selection). De forma similar a Ridge, Lasso también tiene un parámetro de regularización, alpha, que controla la fuerza con la que los coeficientes son empujados hacia cero. En el ejemplo anterior, utilizamos el valor por defecto de alpha=1.0. Para reducir underfitting, intentemos disminuir alpha. Cuando hacemos esto, también necesitamos aumentar el ajuste por defecto de max_iter (número máximo de iteraciones a ejecutar)
lasso001 = Lasso(alpha=0.01, max_iter=100000).fit(X_train, y_train)
print("Training set score: {:.2f}".format(lasso001.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso001.score(X_test, y_test)))
print("Number of features used: {}".format(np.sum(lasso001.coef_ != 0)))
Training set score: 0.90 Test set score: 0.77 Number of features used: 33
Un alpha más bajo nos permitió ajustar un modelo más complejo, que funcionó mejor en los datos de entrenamiento y de prueba. El rendimiento es ligeramente mejor que utilizando Ridge, y estamos utilizando sólo 33 de las 105 características. Esto hace que este modelo sea potencialmente más fácil de entender. Sin embargo, si fijamos alpha demasiado bajo, volvemos a eliminar el efecto de la regularización y acabamos en overfitting, con un resultado similar al de LinearRegression
lasso00001 = Lasso(alpha=0.0001, max_iter=100000).fit(X_train, y_train)
print("Training set score: {:.2f}".format(lasso00001.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso00001.score(X_test, y_test)))
print("Number of features used: {}".format(np.sum(lasso00001.coef_ != 0)))
Training set score: 0.95 Test set score: 0.64 Number of features used: 96
MODELOS LINEALES PARA CLASIFICACIÓN¶
Podemos aplicar los modelos LogisticRegression y LinearSVC al conjunto de datos forge y visualizar la forntera de decisión encontrado por los modelos lineales.A pesar de su nombre, LogisticRegression es un algoritmo de clasificación y no de regresión, por lo tanto no debe confundirse con LinearRegression
sns.set_style("darkgrid") #Esta línea de código establece el estilo de los gráficos creados con
#Seaborn en "darkgrid", lo que significa un estilo con un fondo oscuro y líneas de cuadrícula.
X, y = mglearn.datasets.make_forge()
Este código en Python utiliza las bibliotecas matplotlib, mglearn, y algún modelo de machine learning (como LinearSVC y LogisticRegression) para visualizar los límites de decisión y los datos en un conjunto de datos bidimensional. A continuación, se explica paso a paso lo que hace:
fig, axes = plt.subplots(1, 2, figsize=(15, 5)): Se crea una figura (fig) con dos subplots (axes) dispuestos en una fila (1) y dos columnas (2) y se especifica un tamaño de figura de 15 unidades de ancho y 5 unidades de alto.
Un bucle for itera sobre una lista de modelos (LinearSVC() y LogisticRegression()) junto con los ejes correspondientes (ax) en los que se trazarán los gráficos.
Dentro del bucle for, se ajusta cada modelo (clf = model.fit(X, y)) al conjunto de datos X e y. Esto implica entrenar los modelos de clasificación en los datos bidimensionales X con etiquetas y.
Se utiliza mglearn.plots.plot_2d_separator() para trazar el límite de decisión del modelo en el espacio de características bidimensional (X). Los parámetros fill=False, eps=0.5 y alpha=.7 controlan el estilo de visualización del límite de decisión.
mglearn.discrete_scatter() se utiliza para trazar los puntos de datos (X[:, 0] y X[:, 1]) en el gráfico y colorearlos de acuerdo con las etiquetas de clase y.
ax.set_title(), ax.set_xlabel() y ax.set_ylabel() se utilizan para establecer el título y las etiquetas de los ejes para cada gráfico. El título se establece en función del nombre de la clase del modelo (clf.class.name).
axes[0].legend() agrega una leyenda al primer gráfico para indicar la relación entre los puntos de datos y las clases. Esto se hace solo en el primer gráfico (axes[0]).
fig, axes = plt.subplots(1, 2, figsize=(15, 5))
for model, ax in zip([LinearSVC(), LogisticRegression()], axes):
clf = model.fit(X, y)
mglearn.plots.plot_2d_separator(clf, X, fill=False, eps=0.5, ax=ax, alpha=.7)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
ax.set_title("{}".format(clf.__class__.__name__))
ax.set_xlabel("Feature 0")
ax.set_ylabel("Feature 1")
axes[0].legend()
axes[1].legend()
No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
Para LogisticRegression y LinearSVC el parámetro de compensación que determina la fuerza de la regularización se llama C, y los valores más altos de C corresponden a menor regularización
En otras palabras, cuando se utiliza un valor alto para el parámetro C, LogisticRegression y LinearSVC intentan ajustarse al conjunto de entrenamiento lo mejor posible, mientras que con valores bajos del parámetro C, los modelos ponen más énfasis en encontrar un vector de coeficientes $\beta$ que se acerque a cero. Por defecto, C=1.
Modelos lineales para la clasificación multiclase¶
Muchos modelos de clasificación lineal sólo sirven para la clasificación binaria y no se extienden de forma natural al caso multiclase (con la excepción de la regresión logística). Una técnica común para extender un algoritmo de clasificación binaria a un algoritmo de clasificación multiclase es el enfoque one-vs.-rest. En el enfoque one-vs.-rest, se aprende un modelo binario para cada clase fija, el cual intenta separar esa clase de todas las demás, lo cual da lugar a tantos modelos binarios como clases exista. Para hacer una predicción, se ejecutan todos los clasificadores binarios en un punto de prueba
from sklearn.datasets import make_blobs
X, y = make_blobs(random_state=42)
plt.figure(figsize=(8, 8))
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.legend(["Class 0", "Class 1", "Class 2"]);
Ahora, entrenamos un clasificador LinearSVC en el conjunto de datos
linear_svm = LinearSVC().fit(X, y)
print("Coefficient shape: ", linear_svm.coef_.shape)
print("Intercept shape: ", linear_svm.intercept_.shape)
Coefficient shape: (3, 2) Intercept shape: (3,)
Vemos que la dimensión (shape) de coef_ es (3, 2), lo que significa que cada fila de coef_ contiene el vector de coeficientes para cada una de las tres clases y cada columna contiene el valor del coeficiente para cada característica específica (hay dos en este conjunto de datos). La matriz intercept_ es ahora una matriz unidimensional que almacena los interceptos de cada clase
Visualicemos las líneas dadas por los tres clasificadores binarios. En este caso line=x, para el clasificador separador ax+by+c=0.
máquina de vectores de soporte lineal:
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
line = np.linspace(-15, 15)
for coef, intercept, color in zip(linear_svm.coef_, linear_svm.intercept_, ['b', 'r', 'g']):
plt.plot(line, -(line * coef[0] + intercept) / coef[1], c=color)
plt.ylim(-10, 15)
plt.xlim(-10, 8)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.legend(['Class 0', 'Class 1', 'Class 2', 'Line class 0', 'Line class 1',
'Line class 2'], loc=(1.01, 0.3))